/* 
 * File:   VulnerabilidadService.cpp
 * Author: Maximiliano Monzon (82341)
 * 
 * Created on October 22, 2012, 4:58 AM
 */
#include <sstream>
#include <vector>
#include <arpa/inet.h>
#include "server_VulnerabilidadService.h"
#include "common_ProtocoloConstantes.h"

using namespace std;
using namespace TP::Servicios;

#define COMANDO_INVALIDO 0
#define IP_INVALIDA 0

VulnerabilidadService::VulnerabilidadService( const string& pVulnerabilidadesDataPath, const string& pVulnerabilidadesDefinicionesDataPath ) {
    
    this->vulnerabilidadesDefinicionesPath = pVulnerabilidadesDefinicionesDataPath;
    this->vulnerabilidadesPath = pVulnerabilidadesDataPath;
}
        
VulnerabilidadService::~VulnerabilidadService() {}

void VulnerabilidadService::Iniciar() throw (Excepcion&){
    try
    {
        // Levanto la informacion de Definicion de vulnerabilidades.

        ifstream input (this->vulnerabilidadesDefinicionesPath.c_str(), ifstream::in);
        if (!input.is_open())
            throw Excepcion ("Fallo al abrir el archivo" + this->vulnerabilidadesDefinicionesPath);
        
        while (!input.eof ())
        {        
            string line;
            std::getline(input, line, '\n');
            

            size_t pos = line.find('|', 0);
            if (string::npos != pos)
            {
                string key = line.substr(0, pos);
                string value = line.substr(pos + 1, line.length() - pos + 1);
                this->vulnerabilidadesDefiniciones [key] = value;
            }
        }
        input.close();
        
        // Levanto la informacion de vulnerabilidades.Se determina a que comando corresponde el mensaje.
        
        input.open(this->vulnerabilidadesPath.c_str(), ifstream::in);
        
        if (!input.is_open())
            throw Excepcion ("Fallo al abrir el archivo" + this->vulnerabilidadesPath);
        
        while (!input.eof ())
        {        
            string line;
            std::getline(input, line, '\n');
            

            size_t pos = line.find('|', 0);
            if (string::npos != pos)
            {
                string key = line.substr(0, pos);
                string value = line.substr(pos + 1, line.length() - pos + 1);
                this->vulnerabilidades[key] = value;
            }
        }
        input.close();
    }
    catch (Excepcion& pEx)
    {
        pEx.expandirMensaje("No se puede inicializar el archivo de definicion de vulnerabilidades.");
        throw;
    }
}

void VulnerabilidadService::Finalizar() throw (Excepcion&){

    try
    {
        ofstream stream (this->vulnerabilidadesPath.c_str(), ofstream::out|ofstream::trunc);
        if (!stream.is_open())
            throw Excepcion ("No se puede abrir el archivo " + this->vulnerabilidadesPath + " para actualizar las vulnerabilidades.");
        
        for (map<string,string>::const_iterator it = this->vulnerabilidades.begin(); 
             it != this->vulnerabilidades.end();
             it++)
        {
            string out = it->first + "|" + this->vulnerabilidades[it->first] + "\n";
            stream << out;
        }
    }
    catch (Excepcion& pEx)
    {
        pEx.expandirMensaje("No se puede finalizar el servicio Vulnerabilidades.");
        throw;
    }
}

void VulnerabilidadService::mensajeEntranteManejador(IMensajero* pMensajero, const string & pMensaje){

    // El valor de retorno es error hasta que se demuestre lo contrario.
    string return_msg = "error\n";     

    try
    {
        // Hidrato el comando.

        std::stringstream ssParametros (pMensaje);
        string strParametro = "";
        vector<string> comando;
        while (std::getline(ssParametros, strParametro, SEPARADOR_CAMPO))
        {
            if (strParametro != "" )
            {
                comando.push_back(strParametro);
            }
        }

        if (comando.size() == 0)
            throw COMANDO_INVALIDO;
        

        // Se determina a que comando corresponde el mensaje.
        switch (comando[0].c_str()[0])
        {
            case 'L':
            {
                if (comando.size() == 1)
                {
                    // Se solicito la lista de vulnerabilidades de la red.
                    this->listarVulnerabilidades (pMensajero, comando);
                }
                else if (comando.size() == 2)
                {
                    // El comando corresponde a un listado por host.
                    if (!this->ipValida(comando[1]))
                        throw IP_INVALIDA;
                    
                    // Se solicitaron la lista de vulnerabilidades del host.
                    this->listarVulnerabilidadesPorHost(pMensajero, comando);
                }
                else
                    throw COMANDO_INVALIDO;
                break;
            }
            case 'A':
            {
                if (comando.size() != 3)
                    throw COMANDO_INVALIDO;
                else if (! this->ipValida(comando[1]))
                    throw IP_INVALIDA;
                    
                this->agregarVulnerabilidadAHost(pMensajero, comando);
                break;
            }
            case 'C':
            {
                if (comando.size() != 3)
                    throw COMANDO_INVALIDO;
                else if (! this->ipValida(comando[1]))
                    throw IP_INVALIDA;

                this->removerVulnerabilidadAHost(pMensajero, comando);
                break;
            }
            default:
                // Comando no definido => Error.
                throw COMANDO_INVALIDO;
        }
    }
    catch (int pEx)
    {
        try
        {
            pMensajero->Responder(return_msg);
        }
        catch (Excepcion& pEx)
        {
            cerr << pEx.what() << endl;
        }
        
    }
}

bool VulnerabilidadService::ipValida (const string& pIp) {

    char str_aux [256];

    bool ip_valida = inet_pton(AF_INET, pIp.c_str(), str_aux);

    if (!ip_valida)
        ip_valida = inet_pton(AF_INET6, pIp.c_str(), str_aux);
    
    return ip_valida;
}

void VulnerabilidadService::listarVulnerabilidades (IMensajero* pMensajero, vector<string>& pComando){
    pthread_mutex_lock(&mutex);
    
    string respuesta = "";
    
    for (map<string, string>::const_iterator it = this->vulnerabilidades.begin(); 
         it != this->vulnerabilidades.end();
         it++)
    {
        std::stringstream ss (it->second.c_str());
        std::string item;
        while (std::getline(ss, item, '|'))
            respuesta += it->first + "\t" + item + "\n";
    }
    pthread_mutex_unlock(&mutex);
    
    // Notifico la respuesta a traves del mensajero.    
    try
    {
        if (respuesta.length() > 0)
            pMensajero->Responder(respuesta);
        else
            pMensajero->Responder("error\n");    }
    catch (Excepcion& pEx)
    {
        cerr << pEx.what();
    }
}

void VulnerabilidadService::listarVulnerabilidadesPorHost(IMensajero* pMensajero, vector<string>& pComando){
    
    pthread_mutex_lock(&mutex);

    string mensaje = "";
    
    if (this->vulnerabilidades.count(pComando[1]) > 0)
    {
        // Obtengo el listado de vulnerabilidades del host.
        std::stringstream ss (this->vulnerabilidades[pComando[1]]);
        string item;
        vector<string> info;
        while (std::getline(ss, item, '|'))
            info.push_back(item);

        // Para toda vulnerabilidad asociada al host =>
        // Busco la definicion de la vulnerabilidad.
        // Agrego la definicion de la vulnerabilidad al mensaje.
        
        for (vector<string>::const_iterator it = info.begin(); it != info.end(); it++)
        {
            // Leo la definicion de la vulnerabilidad.
            vector<string> definicion;
            std::stringstream ss_def (this->vulnerabilidadesDefiniciones[*it]);
            while (std::getline(ss_def, item, '|'))
                definicion.push_back(item);

            // Construyo el mensaje.
            if (definicion.size() > 0)
                mensaje += *it + "\t" + definicion [0] + "\n" + definicion [1] + "\n";
        }
    }
    else
        mensaje += "error\n";

    pthread_mutex_unlock(&mutex);

    // Notifico la respuesta a traves del mensajero.    
    try
    {
        pMensajero->Responder(mensaje);
    }
    catch (Excepcion& pEx)
    {
        cerr << pEx.what() << endl;
    }
}

void VulnerabilidadService::agregarVulnerabilidadAHost(IMensajero* pMensajero, vector<string>& pComando){
    
    string mensaje = "error\n";
    
    // Formato del comando: A <IP> <cve>
    
    if (pComando.size() ==  3)
    {
        pthread_mutex_lock(&mutex);

        mensaje = "ok\n";

        if (this->vulnerabilidades.count(pComando[1]) > 0)
        {
            // El equipo ya posee vulnerabilidades.  Agrego la indicada en el comando.
            bool posee = false;
            std::stringstream ss (this->vulnerabilidades[pComando[1]]);
            string item;
            while (std::getline(ss, item, '|') && !posee)
                posee = item == pComando[2];

            if (!posee)
                this->vulnerabilidades[pComando[1]] += pComando[2] + '|';
            else
                mensaje = "error\n";
        }
        else
            // Es la primera vulnerabilidad del host!!! 
            this->vulnerabilidades[pComando[1]] = pComando[2] + '|';

        pthread_mutex_unlock(&mutex);
    }
    try
    {
        pMensajero->Responder(mensaje);
    }
    catch (Excepcion& pEx)
    {
        cerr << pEx.what() << endl;
    }
}

void VulnerabilidadService::removerVulnerabilidadAHost(IMensajero* pMensajero, vector<string>& pComando){

    // Formato del comando: C <IP> <CVE>
    string mensaje = "error\n";
    
    if (pComando.size() == 3)
    {
        pthread_mutex_lock(&mutex);

        if (this->vulnerabilidades.count(pComando[1]) > 0)
        {
            // El equipo existe.  Verifico si posee vulnerabilidades. 
            bool posee = false;
            std::stringstream ss (this->vulnerabilidades[pComando[1]]);
            string item;
            while (std::getline(ss, item, SEPARADOR_CAMPO) && !posee)
                posee = item == pComando[2];

            if (posee)
            {
                // Elimino la vulnerabilidad.
                std::stringstream ss (this->vulnerabilidades[pComando[1]]);
                string item;
                string aux = "";
                while (std::getline(ss, item, SEPARADOR_CAMPO))
                {
                    if (item != pComando[2])
                        aux += item + "|";
                }

                if (aux.length() > 0)
                    this->vulnerabilidades[pComando[1]] = aux;
                else
                    this->vulnerabilidades.erase(pComando[1]);

                mensaje = "ok\n";
            }
        }
        pthread_mutex_unlock(&mutex);
    }
    
    try
    {
        pMensajero->Responder(mensaje);
    }
    catch (Excepcion& pEx)
    {
        cerr << pEx.what() << endl;
    }
}       



